package com.weasing.res.utils;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.PositionInParagraph;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

/**
 *
 * 对docx文件中的文本及表格中的内容进行替换 --模板仅支持对 {key} 标签的替换
 *
 * @ClassName: WordTemplate
 * @Description: TODO(!!!使用word2013 docx文件)
 * @author long
 * @date: <br>
 *        (1)word模板注意页边距的问题，存在问题：比如页边距默认为3cm，画表格时，仍然可以通过
 *        拖拽，把表格边框拖动到看起来就像页边距只有1cm的样子，但是实际上此时页边距还是3cm，生成的
 *        word报表的页边距还是会按照3cm来生成。解决办法，在word文件里，设置好页边距，如果需要表格
 *        两边页边距很窄，需要在word里设置页边距窄一点，而不是直接拖动表格边框来实现。
 *
 */

public class WordTemplate {

	private XWPFDocument document;

	public XWPFDocument getDocument() {
		return document;
	}

	public void setDocument(XWPFDocument document) {
		this.document = document;
	}

	/**
	 * 初始化模板内容
	 *
	 * @author long
	 * @param inputStream
	 *            模板的读取流(docx文件)
	 * @throws IOException
	 *
	 */
	public WordTemplate(InputStream inputStream) throws IOException {
		document = new XWPFDocument(inputStream);
	}

	/**
	 * 将处理后的内容写入到输出流中
	 *
	 * @param outputStream
	 * @throws IOException
	 */
	public void write(OutputStream outputStream) throws IOException {
		document.write(outputStream);
	}

	/**
	 * 替换doc文档 ${}
	 * 
	 * @param 源路劲
	 *            输出路劲 替换文件
	 *
	 */
	public static void replaceDocument(String srcPath, String destPath,
			Map<String, String> map) {

		try {
			XWPFDocument document = new XWPFDocument(
					POIXMLDocument.openPackage(srcPath));
			/**
			 * 替换段落中的指定文字
			 */
			Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();
			while (itPara.hasNext()) {
				XWPFParagraph paragraph = (XWPFParagraph) itPara.next();
				Set<String> set = map.keySet();
				Iterator<String> iterator = set.iterator();
				while (iterator.hasNext()) {
					String key = iterator.next();
					List<XWPFRun> run = paragraph.getRuns();
					for (int i = 0; i < run.size(); i++) {
						if (run.get(i)
								.getText(run.get(i).getTextPosition()) != null
								&&
								run.get(i).getText(run.get(i).getTextPosition())
										.equals(key)) {
							/**
							 * 参数0表示生成的文字是要从哪一个地方开始放置,设置文字从位置0开始 就可以把原来的文字全部替换掉了
							 */
							run.get(i).setText(map.get(key), 0);
						}
					}
				}
			}

			/**
			 * 替换表格中的指定文字
			 */
			Iterator<XWPFTable> itTable = document.getTablesIterator();
			while (itTable.hasNext()) {
				XWPFTable table = (XWPFTable) itTable.next();
				int count = table.getNumberOfRows();
				for (int i = 0; i < count; i++) {
					XWPFTableRow row = table.getRow(i);
					List<XWPFTableCell> cells = row.getTableCells();
					for (XWPFTableCell cell : cells) {
						for (Entry<String, String> e : map.entrySet()) {
							if (cell.getText().equals(e.getKey())) {
								cell.removeParagraph(0);
								cell.setText(e.getValue());
							}
						}
					}
				}
			}
			FileOutputStream outStream = null;
			outStream = new FileOutputStream(destPath);
			document.write(outStream);
			outStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 根据 模板表格 和 数据list 在word文档末尾生成表格
	 * 
	 * @author long
	 * @param templateTable
	 *            模板表格
	 * @param list
	 *            循环数据集
	 * @param parametersMap
	 *            不循环数据集
	 * @param flag
	 *            (0为静态表格，1为表格整体循环，2为表格内部行循环，3为表格不循环仅简单替换标签即可)
	 *
	 */
	public void addTableInDocFooter(XWPFTable templateTable,
			List<Map<String, Object>> list,
			Map<String, Object> parametersMap, int flag) {

		if (flag == 1) {// 表格整体循环
			for (Map<String, Object> map : list) {
				List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行
				XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列
				for (int i = 1; i < templateTableRows.size(); i++) {
					XWPFTableRow newCreateRow = newCreateTable.createRow();
					CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行
				}
				newCreateTable.removeRow(0);// 移除多出来的第一行
				document.createParagraph();// 添加回车换行
				replaceTable(newCreateTable, map);// 替换标签
			}

		} else if (flag == 2) {// 表格表格内部行循环
			XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列
			List<XWPFTableRow> TempTableRows = templateTable.getRows();// 获取模板表格所有行
			int tagRowsIndex = 0;// 标签行indexs
			for (int i = 0, size = TempTableRows.size(); i < size; i++) {
				String rowText = TempTableRows.get(i).getCell(0).getText();// 获取到表格行的第一个单元格
				if (rowText.indexOf("##{foreachRows}##") > -1) {
					tagRowsIndex = i;
					break;
				}
			}

			/* 复制模板行和标签行之前的行 */
			for (int i = 1; i < tagRowsIndex; i++) {
				XWPFTableRow newCreateRow = newCreateTable.createRow();
				CopyTableRow(newCreateRow, TempTableRows.get(i));// 复制行
				replaceTableRow(newCreateRow, parametersMap);// 处理不循环标签的替换
			}

			/* 循环生成模板行 */
			XWPFTableRow tempRow = TempTableRows.get(tagRowsIndex + 1);// 获取到模板行
			for (int i = 0; i < list.size(); i++) {
				XWPFTableRow newCreateRow = newCreateTable.createRow();
				CopyTableRow(newCreateRow, tempRow);// 复制模板行
				replaceTableRow(newCreateRow, list.get(i));// 处理标签替换
			}

			/* 复制模板行和标签行之后的行 */
			for (int i = tagRowsIndex + 2; i < TempTableRows.size(); i++) {
				XWPFTableRow newCreateRow = newCreateTable.createRow();
				CopyTableRow(newCreateRow, TempTableRows.get(i));// 复制行
				replaceTableRow(newCreateRow, parametersMap);// 处理不循环标签的替换
			}
			newCreateTable.removeRow(0);// 移除多出来的第一行
			document.createParagraph();// 添加回车换行

		} else if (flag == 3) {
			// 表格不循环仅简单替换标签
			List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行
			XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列
			for (int i = 0; i < templateTableRows.size(); i++) {
				XWPFTableRow newCreateRow = newCreateTable.createRow();
				CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行
			}
			newCreateTable.removeRow(0);// 移除多出来的第一行
			document.createParagraph();// 添加回车换行
			replaceTable(newCreateTable, parametersMap);

		} else if (flag == 0) {
			List<XWPFTableRow> templateTableRows = templateTable.getRows();// 获取模板表格所有行
			XWPFTable newCreateTable = document.createTable();// 创建新表格,默认一行一列
			for (int i = 0; i < templateTableRows.size(); i++) {
				XWPFTableRow newCreateRow = newCreateTable.createRow();
				CopyTableRow(newCreateRow, templateTableRows.get(i));// 复制模板行文本和样式到新行
			}
			newCreateTable.removeRow(0);// 移除多出来的第一行
			document.createParagraph();// 添加回车换行
		}

	}

	/**
	 * 根据 模板段落 和 数据 在文档末尾生成段落
	 *
	 * @author long
	 * @param templateParagraph
	 *            模板段落
	 * @param list
	 *            循环数据集
	 * @param parametersMap
	 *            不循环数据集
	 * @param flag
	 *            (0为不循环替换，1为循环替换)
	 *
	 */
	public void addParagraphInDocFooter(XWPFParagraph templateParagraph,
			List<Map<String, String>> list, Map<String, Object> parametersMap,
			int flag) {

		if (flag == 0) {
			XWPFParagraph createParagraph = document.createParagraph();
			// 设置段落样式
			createParagraph.getCTP()
					.setPPr(templateParagraph.getCTP().getPPr());
			// 移除原始内容
			for (int pos = 0; pos < createParagraph.getRuns().size(); pos++) {
				createParagraph.removeRun(pos);
			}
			// 添加Run标签
			for (XWPFRun s : templateParagraph.getRuns()) {
				XWPFRun targetrun = createParagraph.createRun();
				CopyRun(targetrun, s);
			}

			replaceParagraph(createParagraph, parametersMap);

		} else if (flag == 1) {
			// 暂无实现
		}

	}

	/**
	 * 根据map替换段落元素内的{**}标签
	 * 
	 * @author long
	 * @date 2017年12月4日 下午3:09:00
	 * @param xWPFParagraph
	 * @param parametersMap
	 *
	 */
	public void replaceParagraph(XWPFParagraph xWPFParagraph,
			Map<String, Object> parametersMap) {
		List<XWPFRun> runs = xWPFParagraph.getRuns();
		String xWPFParagraphText = xWPFParagraph.getText();
		String regEx = "\\{.+?\\}";
		Pattern pattern = Pattern.compile(regEx);
		Matcher matcher = pattern.matcher(xWPFParagraphText);// 正则匹配字符串{****}

		if (matcher.find()) {
			// 查找到有标签才执行替换
			int beginRunIndex = xWPFParagraph
					.searchText("{", new PositionInParagraph()).getBeginRun();// 标签开始run位置
			int endRunIndex = xWPFParagraph
					.searchText("}", new PositionInParagraph()).getEndRun();// 结束标签
			StringBuffer key = new StringBuffer();

			if (beginRunIndex == endRunIndex) {
				// {**}在一个run标签内
				XWPFRun beginRun = runs.get(beginRunIndex);
				String beginRunText = beginRun.text();

				int beginIndex = beginRunText.indexOf("{");
				int endIndex = beginRunText.indexOf("}");
				int length = beginRunText.length();

				if (beginIndex == 0 && endIndex == length - 1) {
					// 该run标签只有{**}
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(beginRunIndex);
					insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
					// 设置文本
					key.append(beginRunText.substring(1, endIndex));
					insertNewRun.setText(
							getValueBykey(key.toString(), parametersMap));
					xWPFParagraph.removeRun(beginRunIndex + 1);
				} else {
					// 该run标签为**{**}** 或者 **{**} 或者{**}**，替换key后，还需要加上原始key前后的文本
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(beginRunIndex);
					insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
					// 设置文本
					key.append(beginRunText.substring(
							beginRunText.indexOf("{") + 1,
							beginRunText.indexOf("}")));
					String textString = beginRunText.substring(0, beginIndex)
							+ getValueBykey(key.toString(), parametersMap)
							+ beginRunText.substring(endIndex + 1);
					insertNewRun.setText(textString);
					xWPFParagraph.removeRun(beginRunIndex + 1);
				}

			} else {
				// {**}被分成多个run

				// 先处理起始run标签,取得第一个{key}值
				XWPFRun beginRun = runs.get(beginRunIndex);
				String beginRunText = beginRun.text();
				int beginIndex = beginRunText.indexOf("{");
				if (beginRunText.length() > 1) {
					key.append(beginRunText.substring(beginIndex + 1));
				}
				ArrayList<Integer> removeRunList = new ArrayList<>();// 需要移除的run
				// 处理中间的run
				for (int i = beginRunIndex + 1; i < endRunIndex; i++) {
					XWPFRun run = runs.get(i);
					String runText = run.text();
					key.append(runText);
					removeRunList.add(i);
				}

				// 获取endRun中的key值
				XWPFRun endRun = runs.get(endRunIndex);
				String endRunText = endRun.text();
				int endIndex = endRunText.indexOf("}");
				// run中**}或者**}**
				if (endRunText.length() > 1 && endIndex != 0) {
					key.append(endRunText.substring(0, endIndex));
				}

				// *******************************************************************
				// 取得key值后替换标签

				// 先处理开始标签
				if (beginRunText.length() == 2) {
					// run标签内文本{
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(beginRunIndex);
					insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
					// 设置文本
					insertNewRun.setText(
							getValueBykey(key.toString(), parametersMap));
					xWPFParagraph.removeRun(beginRunIndex + 1);// 移除原始的run
				} else {
					// 该run标签为**{**或者 {** ，替换key后，还需要加上原始key前的文本
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(beginRunIndex);
					insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr());
					// 设置文本
					String textString = beginRunText.substring(0,
							beginRunText.indexOf("{"))
							+ getValueBykey(key.toString(), parametersMap);
					insertNewRun.setText(textString);
					xWPFParagraph.removeRun(beginRunIndex + 1);// 移除原始的run
				}

				// 处理结束标签
				if (endRunText.length() == 1) {
					// run标签内文本只有}
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(endRunIndex);
					insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());
					// 设置文本
					insertNewRun.setText("");
					xWPFParagraph.removeRun(endRunIndex + 1);// 移除原始的run

				} else {
					// 该run标签为**}**或者 }** 或者**}，替换key后，还需要加上原始key后的文本
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(endRunIndex);
					insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr());
					// 设置文本
					String textString = endRunText
							.substring(endRunText.indexOf("}") + 1);
					insertNewRun.setText(textString);
					xWPFParagraph.removeRun(endRunIndex + 1);// 移除原始的run
				}

				// 处理中间的run标签
				for (int i = 0; i < removeRunList.size(); i++) {
					XWPFRun xWPFRun = runs.get(removeRunList.get(i));// 原始run
					XWPFRun insertNewRun = xWPFParagraph
							.insertNewRun(removeRunList.get(i));
					insertNewRun.getCTR().setRPr(xWPFRun.getCTR().getRPr());
					insertNewRun.setText("");
					xWPFParagraph.removeRun(removeRunList.get(i) + 1);// 移除原始的run
				}

			} // 处理${**}被分成多个run

			replaceParagraph(xWPFParagraph, parametersMap);

		} // if 有标签

	}

	/**
	 * 复制表格行XWPFTableRow格式
	 *
	 * @param target
	 *            待修改格式的XWPFTableRow
	 * @param source
	 *            模板XWPFTableRow
	 */
	private void CopyTableRow(XWPFTableRow target, XWPFTableRow source) {

		int tempRowCellsize = source.getTableCells().size();// 模板行的列数
		for (int i = 0; i < tempRowCellsize - 1; i++) {
			target.addNewTableCell();// 为新添加的行添加与模板表格对应行行相同个数的单元格
		}
		// 复制样式
		target.getCtRow().setTrPr(source.getCtRow().getTrPr());
		// 复制单元格
		for (int i = 0; i < target.getTableCells().size(); i++) {
			copyTableCell(target.getCell(i), source.getCell(i));
		}
	}

	/**
	 * 复制单元格XWPFTableCell格式
	 *
	 * @author long
	 * @param newTableCell
	 *            新创建的的单元格
	 * @param templateTableCell
	 *            模板单元格
	 *
	 */
	private void copyTableCell(XWPFTableCell newTableCell,
			XWPFTableCell templateTableCell) {
		// 列属性
		newTableCell.getCTTc().setTcPr(templateTableCell.getCTTc().getTcPr());
		// 删除目标 targetCell 所有文本段落
		for (int pos = 0; pos < newTableCell.getParagraphs().size(); pos++) {
			newTableCell.removeParagraph(pos);
		}
		// 添加新文本段落
		for (XWPFParagraph sp : templateTableCell.getParagraphs()) {
			XWPFParagraph targetP = newTableCell.addParagraph();
			copyParagraph(targetP, sp);
		}
	}

	/**
	 * 复制文本段落XWPFParagraph格式
	 *
	 * @author long
	 * @param newParagraph
	 *            新创建的的段落
	 * @param templateParagraph
	 *            模板段落
	 *
	 */
	private void copyParagraph(XWPFParagraph newParagraph,
			XWPFParagraph templateParagraph) {
		// 设置段落样式
		newParagraph.getCTP().setPPr(templateParagraph.getCTP().getPPr());
		// 添加Run标签
		for (int pos = 0; pos < newParagraph.getRuns().size(); pos++) {
			newParagraph.removeRun(pos);

		}
		for (XWPFRun s : templateParagraph.getRuns()) {
			XWPFRun targetrun = newParagraph.createRun();
			CopyRun(targetrun, s);
		}

	}

	/**
	 * 复制文本节点run
	 * 
	 * @author long
	 * @date 2017年11月27日 下午3:47:17
	 * @param newRun
	 *            新创建的的文本节点
	 * @param templateRun
	 *            模板文本节点
	 *
	 */
	private void CopyRun(XWPFRun newRun, XWPFRun templateRun) {
		newRun.getCTR().setRPr(templateRun.getCTR().getRPr());
		// 设置文本
		newRun.setText(templateRun.text());

	}

	/**
	 * 根据参数parametersMap对表格的一行进行标签的替换
	 *
	 * @author long
	 * @param tableRow
	 *            表格行
	 * @param parametersMap
	 *            参数map
	 *
	 */
	public void replaceTableRow(XWPFTableRow tableRow,
			Map<String, Object> parametersMap) {

		List<XWPFTableCell> tableCells = tableRow.getTableCells();
		for (XWPFTableCell xWPFTableCell : tableCells) {
			List<XWPFParagraph> paragraphs = xWPFTableCell.getParagraphs();
			for (XWPFParagraph xwpfParagraph : paragraphs) {

				replaceParagraph(xwpfParagraph, parametersMap);
			}
		}

	}

	/**
	 * 根据map替换表格中的{key}标签
	 * 
	 * @author long
	 * @param xwpfTable
	 * @param parametersMap
	 *
	 */
	public void replaceTable(XWPFTable xwpfTable,
			Map<String, Object> parametersMap) {
		List<XWPFTableRow> rows = xwpfTable.getRows();
		for (XWPFTableRow xWPFTableRow : rows) {
			List<XWPFTableCell> tableCells = xWPFTableRow.getTableCells();
			for (XWPFTableCell xWPFTableCell : tableCells) {
				List<XWPFParagraph> paragraphs2 = xWPFTableCell.getParagraphs();
				for (XWPFParagraph xWPFParagraph : paragraphs2) {
					replaceParagraph(xWPFParagraph, parametersMap);
				}
			}
		}

	}

	private String getValueBykey(String key, Map<String, Object> map) {
		String returnValue = "";
		if (key != null) {
			try {
				returnValue = map.get(key) != null ? map.get(key).toString()
						: "";
			} catch (Exception e) {
				// TODO: handle exception
				System.out.println("key:" + key + "***" + e);
				returnValue = "";
			}
		}
		return returnValue;
	}

}